package progetto.socialnetwork;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;


public class Loader {
	private Twitter twitter, twitterPool[];
	private long nodoIniziale=396030643; //giuseVacatello
	private int nodiDaEspandere=4, //followers da espandere 
				maxFoll=0; //max followers per nodo (0=tutti)
	
	
	public Loader(){
		init();
	}
	
	public Loader (int nodiDaEspandere, int maxFoll){
		this();
		this.nodiDaEspandere=nodiDaEspandere;
		this.maxFoll=maxFoll;
	}
	
	public Loader (int nodiDaEspandere, int maxFoll, long nodoIniziale){
		this();
		this.nodiDaEspandere=nodiDaEspandere;
		this.maxFoll=maxFoll;
		this.nodoIniziale=nodoIniziale;
	}
	
	private void init() {
		twitterPool=new Twitter[7];
		twitterPool[0]=new TwitterFactory().getInstance();
		
		twitterPool[1] = new TwitterFactory().getInstance();
		twitterPool[1].setOAuthConsumer("4ZHtVgwFVK1WylMZYxj3HA",
				"pzr2lwgXdO4bFVhkN4H1SbbKZbd8v8dsOtkr7Ma6vc");
		twitterPool[1].setOAuthAccessToken(new AccessToken(
				"396030643-vNuJ2qC8B9iOAYyz3yuNbNTH9UMFZAmgBQ3g4pEL",
				"FKPfsrBatL9CG97U941Qka5E5ZcwDGVu1vWxFTDOuM"));	
		
		twitterPool[2] = new TwitterFactory().getInstance();
		twitterPool[2].setOAuthConsumer("K5nrKBJESsSjhYFqYgA",
				"BQ9RnqRKtmCd1fDPozOSL3O6zGa1Fu9IOFrqXt2Qkg");
		twitterPool[2].setOAuthAccessToken(new AccessToken(
				"712472600-f5SFRn6zerB4SlGE7iafkIJe1HzduOmoYxrfCUWT",
				"3x5Lgwj9oxefAoZhaijVFpX1RDAjIgqJw8zMFriBYRo"));
		
		twitterPool[3] = new TwitterFactory().getInstance();
		twitterPool[3].setOAuthConsumer("LKK1yApukpJwIWaWrCFgw",
				"24jsDhDZyQebTGWXIr1oWINK6IJlb4oO6OFDmuPvmQQ");
		twitterPool[3].setOAuthAccessToken(new AccessToken(
				"712496642-6ZGlyvHfk8Gq6pbs3Ql7bDA5XohH05SW0WXuVCN1",
				"Zdxn5noNodKLyYEzkmYUPi7efe7o6VNwRa0OshZqU"));
		
		twitterPool[4] = new TwitterFactory().getInstance();
		twitterPool[4].setOAuthConsumer("wRB3g9qbQL2M6A8FgZAdBw",
				"Y9k44bFeb8IdxOxJrtLkikyM6DXs0quvJoWccFIXug");
		twitterPool[4].setOAuthAccessToken(new AccessToken(
				"712617823-kBRZkGx47246aGPHuzAZqUDRjfoZqKOC1Ya1G7mK",
				"9Z9ErKg6TfLncNNJeoso5SILhf0aq35wIDFj8lyoYk"));
				
		twitterPool[5] = new TwitterFactory().getInstance();
		twitterPool[5].setOAuthConsumer("U0GIUqqoa23AoTwzcynaUw",
				"zJmnE6n9qbWzeZuisJmB6tP3DZ5teXxWgeY7uyBQ");
		twitterPool[5].setOAuthAccessToken(new AccessToken(
				"712632042-zjz8YdYM1vtMRIbmy0iyAJL0pbtBjpyilYePgLZj",
				"HZqNZLa2ao8Hwd9xL0Imj7N5LOol0cWcKe8zWajl8c"));
		
		twitterPool[6] = new TwitterFactory().getInstance();
		twitterPool[6].setOAuthConsumer("alncGcc2Zi8yz7cqMC1wmw",
				"mvoYjh8kuRVcUjLV6yEcw8GlAliBkUdWYvAHIU4ETdo");
		twitterPool[6].setOAuthAccessToken(new AccessToken(
				"50298255-sIkplF0B6USzG5FB8x9JaoPcA4eZGSUTTbGYoxzQk",
				"C7kbmN7M93SMF4ZTl0oTLDgg4Hr4tIdyvKa7Bbdt8"));		
		
		twitter = twitterPool[0];
	}
	
	public boolean switchAccount(){
		try {
			if (twitter.getRateLimitStatus().getRemainingHits()==0){//Provo a switchare
				int minTime=twitter.getRateLimitStatus().getSecondsUntilReset();
				int minIndex=-1;
				for (int i=0; i<twitterPool.length; i++){
					if (twitterPool[i]!=twitter){
						if (twitterPool[i].getRateLimitStatus().getRemainingHits()>0){
							twitter=twitterPool[i];
							return true;
						}
						int currTime=twitterPool[i].getRateLimitStatus().getSecondsUntilReset();
						if (minTime>currTime){
							minTime=currTime;
							minIndex=i;
						}
					}
				}
				if (minIndex>=0){
					twitter=twitterPool[minIndex];
					return true;
				}
			}
		} catch (TwitterException e) {}
		return false;
	}

	public void queryRimanenti() {
		try {
			int queryTotali=0;
			for (int i=0; i<twitterPool.length; i++){
				int queryCorr=twitterPool[i].getRateLimitStatus().getRemainingHits();
				queryTotali+=queryCorr;
			System.out.print("Query rimanenti ("+i+"): " + queryCorr
					+" / "+twitterPool[i].getRateLimitStatus().getHourlyLimit()+"  	");			
			System.out.println("Minuti al reset: "
					+ (twitterPool[i].getRateLimitStatus().getSecondsUntilReset() / 60));
			}
			System.out.println("Query totali a disposizione "+queryTotali);
		} catch (TwitterException te) {
		}
	}
	
	public void caricaDatiDaTwitter(Dati d, int n, int seme) {
		int nodiAggiunti = 0;
		Queue<Long> q = new LinkedList<Long>(); //Coda di Id Utente
		try {			
			long cursor = -1;
			long[] ids = null;
			long in = System.currentTimeMillis();
			q.offer(nodoIniziale);
			boolean finito = false;
			while (!q.isEmpty() && !finito) {
				long idCorr = q.poll();
				boolean errore = true;
				while (errore) {
					try {
						ids = twitter.getFollowersIDs(idCorr, cursor).getIDs();
						errore = false;
					} catch (TwitterException te) {
						// verifico se ho finito le query
						if (twitter.getRateLimitStatus().getRemainingHits() == 0) {
							if (switchAccount()){continue;} //Cambio account
							int sec = twitter.getRateLimitStatus().getSecondsUntilReset();
							System.out.println("Numero di query massime raggiunte. Attendo "+
									timeToString(sec*1000));
							try {Thread.sleep(sec * 100);} catch (InterruptedException e) {}
						} else {// Skip
							System.out.println("Non è possibile effettuare query sul nodo "+ idCorr);
							break;
						}
						errore = true;
					}
				}

				if (!d.getNodi().contains(idCorr)) {nodiAggiunti++;}
				
				//Verifico se devo tagliare sui followers
				if (maxFoll>0 && ids.length>maxFoll){
					long idsTrim[]=new long[maxFoll];
					for (int i=0;i<maxFoll; i++){
						idsTrim[i]=ids[i];
					}
					ids=idsTrim;
				}
				
				//Verifico se super il limite di nodi				
				if (ids.length + nodiAggiunti > n) {
					// TRIM
					int nodiDaAggiungere = n - nodiAggiunti;
					int dimV = 0;
					for (int i = 0; i < ids.length; i++) {
						if (nodiDaAggiungere == 0) {
							break;
						}
						dimV++;
						if (!d.getNodi().contains(ids[i])) {
							nodiDaAggiungere--;
						}
					}
					if (dimV < ids.length) {
						long idsTrim[] = new long[dimV];
						for (int i = 0; i < idsTrim.length; i++) {
							idsTrim[i] = ids[i];
						}
						ids = idsTrim;
						finito = true;
					}
				}

				d.aggiungiFollowers(idCorr, ids);
				nodiAggiunti = d.getNodi().size();

				System.out.println("Ho aggiunto il nodo " + idCorr + " con "+ ids.length + " followers");

				for (int i = 0; i < nodiDaEspandere && i < ids.length; i++) {
					int rnd = (seme + i) % ids.length;
					long idScelto = ids[rnd];
					q.offer(idScelto);
				}

			} // While (!finito)

			System.out.println("Tempo totale per l'estrazione di "+ d.getNodi().size() + " nodi "
							   + timeToString(System.currentTimeMillis() - in)); 
		} catch (TwitterException te) {
			te.printStackTrace();
			System.out.println("Errore: " + te.getMessage());
		}
	}


	public void caricaSolamenteArchi(Dati d) {
		Set<Long> nodiSet = d.getNodi();
		try {			
			long cursor = -1;
			long[] ids = null;
			long in = System.currentTimeMillis();
			Iterator<Long> it = nodiSet.iterator();
			int i = 1;
			while (it.hasNext()) {
				long idCorr = it.next();
				boolean errore = true; 

				while (errore) {
					try {
						ids = twitter.getFriendsIDs(idCorr, cursor).getIDs();
						errore = false;
					} catch (TwitterException te) {
						// verifico se ho finito le query
						if (twitter.getRateLimitStatus().getRemainingHits() == 0) {
							if (switchAccount()){continue;}							
							int sec = twitter.getRateLimitStatus().getSecondsUntilReset();
							System.out.println("Numero di query massime raggiunte. Attendo "+timeToString(sec*1000));
							try {Thread.sleep(sec*100);} catch (InterruptedException e) {}
						} else {// SKIP NODE
							System.out.println(i+". Non è possibile effettuare query sul nodo "+ idCorr);
							errore = true;
							break;
						}
						errore = true;
					}
				}
				if (errore){i++; continue;} //Errore precedente non risolto, proseguo con il prossimo nodo
				int nfoll=ids.length;
				HashSet<Long> set = new HashSet<Long>();
				for (int j = 0; j < ids.length; j++) {
					if (nodiSet.contains(ids[j])) {set.add((Long) ids[j]);}
				}
				ids = new long[set.size()];
				Object idsO[] = set.toArray();
				for (int j = 0; j < ids.length; j++) {ids[j] = (Long) idsO[j];}
				System.out.println(i + ". Sto aggiungendo " + ids.length+ " archi su "+nfoll+" following");
				d.aggiungiFollowing(idCorr, ids);
				i++;
			}
			System.out.println("Tempo totale per l'aggiunta di "+ d.numArchiInUscitaValues() + " archi: "
					   + timeToString(System.currentTimeMillis() - in)); 
		} catch (TwitterException te) {
			te.printStackTrace();
			System.out.println("Errore: " + te.getMessage());
		}
		
	}
	
	public static String timeToString(long msec){
		msec=msec/1000;
		String ret=new String();
		ret+=msec%60+"s";
		msec=msec/60;
		if (msec==0) return ret;
		ret=msec%60+"m "+ret;
		msec=msec/60;
		if (msec==0) return ret;
		ret=msec%24+"h "+ret;
		msec=msec/24;
		if (msec==0) return ret;
		ret=msec+"g "+ret;
		return ret;
	}

	
	public static void unisciDueSetDiDati() {
		Dati d1 = new Dati();
		Dati d2 = new Dati();
		d1.carica();
		d2.carica();
		d1.stampaSize();
		d2.stampaSize();
		d1.merge(d2);
		d1.stampaSize();
		d1.salva();
	}
	
}
